package com.gxar.uaa.server.db.service;

import cn.hutool.core.util.ObjectUtil;
import com.gxar.common.entity.login.LoginUserThread;
import com.gxar.common.entity.login.UserProfile;
import com.gxar.common.exception.APIRuntimeException;
import com.gxar.uaa.api.dto.menu.SysMenu;
import com.gxar.uaa.api.dto.role.SysRole;
import com.gxar.uaa.api.dto.user.SysUserId;
import com.gxar.uaa.api.enums.MenuTypeEnums;
import com.gxar.uaa.server.base.common.constant.CommonConstant;
import com.gxar.uaa.server.base.model.ResultCodeEnum;
import com.gxar.uaa.server.base.model.menu.mapper.MenuMapStruct;
import com.gxar.uaa.server.base.model.menu.vo.BaseMenuVO;
import com.gxar.uaa.server.base.model.menu.vo.request.MenuCreateRequest;
import com.gxar.uaa.server.base.model.menu.vo.request.MenuUpdateRequest;
import com.gxar.uaa.server.base.model.menu.vo.response.MenuCreateResponse;
import com.gxar.uaa.server.base.model.menu.vo.response.MenuInfoResponse;
import com.gxar.uaa.server.base.model.menu.vo.response.MenuTreeResponse;
import com.gxar.uaa.server.base.model.menu.vo.response.MenuUpdateResponse;
import com.gxar.uaa.server.db.mapper.SysMenuMapper;
import com.gxar.uaa.server.db.mapper.SysRoleMenuMapper;
import com.gxar.uaa.server.db.mapper.SysUserRoleMapper;
import com.gxar.uaa.server.db.model.SysMenuEntity;
import com.gxar.uaa.server.db.model.SysRoleMenuEntity;
import com.gxar.uaa.server.db.repository.SysMenuRepository;
import com.gxar.uaa.server.db.repository.SysRoleMenuRepository;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author linsy
 * @version 1.0
 * @created 2024/1/23 10:20
 */
@Service
@RequiredArgsConstructor
public class MenuService {

    private final SysRoleMenuMapper sysRoleMenuMapper;
    private final SysMenuMapper sysMenuMapper;
    private final SysUserRoleMapper userRoleMapper;

    private final SysMenuRepository sysMenuRepository;
    private final SysRoleMenuRepository roleMenuRepository;

    /**
     * 构建菜单树
     *
     * @param sysMenus 菜单
     * @return 菜单树
     */
    public static List<MenuTreeResponse> treeBuilder(List<MenuTreeResponse> sysMenus) {
        List<MenuTreeResponse> menus = new ArrayList<>();
        for (MenuTreeResponse sysMenu : sysMenus) {
            if (ObjectUtil.equal(-1L, sysMenu.getParentId())) {
                menus.add(sysMenu);
            }
            for (MenuTreeResponse menu : sysMenus) {
                if (menu.getParentId().equals(sysMenu.getId())) {
                    if (sysMenu.getSubMenus() == null) {
                        sysMenu.setSubMenus(new ArrayList<>());
                    }
                    sysMenu.getSubMenus().add(menu);
                }
            }
        }
        return menus;
    }

    /**
     * 根据角色code查询
     *
     * @param app       app
     * @param roleCodes 角色code
     * @param type      类型
     * @return 菜单
     */
    public List<SysMenu> findByRoleCodes(String app, Set<String> roleCodes, Integer type) {
        // 当前角色是超级管理员，拥有所有权限
        if (roleCodes.contains(CommonConstant.ADMIN_ROLE_NAME)) {
            return sysMenuMapper.findMenusByType(app, type);
        }
        return sysRoleMenuMapper.findMenusByRoleCodes(app, roleCodes, type);
    }

    /**
     * 根据id删除
     *
     * @param id 菜单id
     */
    public void removeById(Long id) {
        UserProfile userProfile = LoginUserThread.get();
        List<SysMenu> sysMenu = sysMenuMapper.findByParentId(userProfile.getApp(), id);
        if (CollectionUtils.isNotEmpty(sysMenu)) {
            throw new APIRuntimeException(ResultCodeEnum.SUBMENU_EXISTS_NOT_DELETE);
        }
        sysMenuRepository.deleteById(id);
    }

    /***
     * 根据角色id集合查询菜单
     *
     * @param roleIds 角色id集合
     * @return 菜单
     */
    public List<SysMenu> findByRoles(Set<Long> roleIds) {
        return sysRoleMenuMapper.findMenusByRoleIds(LoginUserThread.get().getApp(), roleIds, null);
    }

    /**
     * 查询所有菜单
     *
     * @return 所有菜单
     */
    public List<SysMenu> findAll(Integer type) {
        return sysRoleMenuMapper.findAll(LoginUserThread.get().getApp(), type);
    }

    /**
     * 角色分配菜单
     *
     * @param roleId      角色id
     * @param menuIds     菜单id
     * @param halfMenuIds 半选菜单id
     */
    @Transactional(rollbackFor = Throwable.class)
    public void setMenuToRole(Long roleId, Set<Long> menuIds, Set<Long> halfMenuIds) {
        sysRoleMenuMapper.delete(roleId, null);
        List<SysRoleMenuEntity> roleMenu = new ArrayList<>(menuIds.size());
        if (CollectionUtils.isNotEmpty(menuIds)) {
            menuIds.forEach(e -> {
                SysRoleMenuEntity entity = new SysRoleMenuEntity();
                entity.setMenuId(e);
                entity.setRoleId(roleId);
                entity.setType(0);
                roleMenu.add(entity);
            });
        }
        if (CollectionUtils.isNotEmpty(halfMenuIds)) {
            halfMenuIds.forEach(e -> {
                SysRoleMenuEntity entity = new SysRoleMenuEntity();
                entity.setMenuId(e);
                entity.setRoleId(roleId);
                entity.setType(1);
                roleMenu.add(entity);
            });
        }
        sysRoleMenuMapper.saveAll(roleMenu);
    }

    /**
     * 查询所有菜单列表
     *
     * @return 响应
     */
    public List<MenuInfoResponse> pageList(Integer type) {
        List<SysMenu> sysMenus = findAll(type);
        return MenuMapStruct.INSTANCE.sysMenuToInfoResponseList(sysMenus);
    }

    /**
     * 查询顶级菜单
     *
     * @return 响应
     */
    public List<MenuInfoResponse> findOnes() {
        List<SysMenu> menusOnes = sysMenuMapper.findMenusByType(LoginUserThread.get().getApp(), 1);
        return MenuMapStruct.INSTANCE.sysMenuToInfoResponseList(menusOnes);
    }

    /**
     * 查询用户id菜单树
     *
     * @param userId 用户id
     * @param type   类型
     * @return
     */
    public List<MenuTreeResponse> findTreeByUserId(Long userId, Integer type) {
        return findByUserId(userId, type);
//        return treeBuilder(treeResponses);
    }

    /**
     * 查询当前用户菜单
     *
     * @param userId 用户id
     * @param type   类型
     * @return 菜单树
     */
    public List<MenuTreeResponse> findByUserId(Long userId, Integer type) {
        List<SysRole> userRole = userRoleMapper.findRolesByUserId(LoginUserThread.get().getApp(), userId);
        Optional<SysRole> adminRole = Optional.ofNullable(userRole).orElse(Collections.emptyList()).stream()
                .filter(e -> CommonConstant.ADMIN_ROLE_NAME.equals(e.getCode())).findAny();
        List<SysMenu> sysMenus = sysMenuMapper.findByUserId(LoginUserThread.get().getApp(), userId, type, adminRole.isPresent());
        return MenuMapStruct.INSTANCE.sysMenuVoToTreeResponses(sysMenus);
    }

    /**
     * 创建菜单
     *
     * @param user    创建人
     * @param request 请求
     * @return 响应
     */
    public MenuCreateResponse create(UserProfile user, MenuCreateRequest request) {
        this.checkMenuExist(request);
        SysMenuEntity menuEntity = MenuMapStruct.INSTANCE.createToMenu(request);
        if (Objects.isNull(menuEntity.getParentId())) {
            menuEntity.setParentId(-1L);
        }
        menuEntity.setCreatedAt(new Date());
        menuEntity.setCreator(user.getMobile());
        menuEntity.setUpdatedAt(new Date());
        menuEntity.setDeleted(false);
        SysMenuEntity saved = sysMenuRepository.save(menuEntity);
        return MenuMapStruct.INSTANCE.menuToCreate(saved);
    }

    /**
     * 更新菜单
     *
     * @param user    更新人
     * @param id      菜单id
     * @param request 请求
     * @return
     */
    public MenuUpdateResponse update(UserProfile user, Long id, MenuUpdateRequest request) {
        Optional<SysMenuEntity> menuEntityOptional = sysMenuRepository.findById(id);
        if (!menuEntityOptional.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.MENU_NOT_EXIST);
        }
        Integer type = request.getType();
        SysMenuEntity sysMenuEntity = menuEntityOptional.get();
        if (MenuTypeEnums.MENU.getCode().equals(type) || MenuTypeEnums.DIRECTORY.getCode().equals(type)) {
            if (!sysMenuEntity.getName().equals(request.getName())) {
                this.checkMenuExist(request);
            }
        }
        if (MenuTypeEnums.BUTTON.getCode().equals(type)) {
            if (!sysMenuEntity.getUrl().equals(request.getUrl())
                    && !sysMenuEntity.getUrlMethod().equals(request.getUrlMethod())) {
                this.checkMenuExist(request);
            }
        }
        SysMenuEntity menuEntity = MenuMapStruct.INSTANCE.updateToMenu(request);
        menuEntity.setId(id);
        menuEntity.setCreator(sysMenuEntity.getCreator());
        menuEntity.setCreatedAt(sysMenuEntity.getCreatedAt());
        menuEntity.setUpdatedAt(new Date());
        menuEntity.setOperator(user.getMobile());
        menuEntity.setDeleted(false);
        SysMenuEntity saved = sysMenuRepository.save(menuEntity);
        return MenuMapStruct.INSTANCE.menuToUpdate(saved);
    }

    /**
     * 校验菜单是否存在
     *
     * @param request 请求
     * @param <T>     vo
     */
    public <T extends BaseMenuVO> void checkMenuExist(T request) {
        Integer type = request.getType();
        if (MenuTypeEnums.MENU.getCode().equals(type) || MenuTypeEnums.DIRECTORY.getCode().equals(type)) {
            // 校验名称是否重复
            SysMenu sysMenu = sysMenuMapper.findByName(request.getApp(), request.getName());
            if (Objects.nonNull(sysMenu)) {
                throw new APIRuntimeException(ResultCodeEnum.MENU_NAME_EXIST);
            }
        }
        if (MenuTypeEnums.BUTTON.getCode().equals(type)) {
            // 校验url是否重复
            SysMenu sysMenu = sysMenuMapper.findByUrlAndMethod(request.getApp(), request.getUrl(), request.getUrlMethod());
            if (Objects.nonNull(sysMenu)) {
                throw new APIRuntimeException(ResultCodeEnum.MENU_URL_EXIST);
            }
        }
    }

    /**
     * 根据菜单url和方法查询有权限的用户ID
     *
     * @param url       url
     * @param urlMethod url方法
     * @return 响应
     */
    public List<SysUserId> findUserByMenuUrl(String url, String urlMethod) {
        return sysMenuMapper.findUserByMenuUrl(url, urlMethod);
    }

    /**
     * 查询子菜单
     *
     * @param userId   用户ID
     * @param parentId 菜单ID
     * @param type     菜单
     * @return 响应
     */
    public List<MenuInfoResponse> findChildMenu(Long userId, Long parentId, Integer type) {
        List<SysRole> userRole = userRoleMapper.findRolesByUserId(LoginUserThread.get().getApp(), userId);
        Optional<SysRole> adminRole = Optional.ofNullable(userRole).orElse(Collections.emptyList()).stream()
                .filter(e -> CommonConstant.ADMIN_ROLE_NAME.equals(e.getCode())).findAny();
        List<SysMenu> sysMenus = sysMenuMapper.findChildByUserId(LoginUserThread.get().getApp(), parentId, userId, type, adminRole.isPresent());
        return MenuMapStruct.INSTANCE.sysMenuToInfoResponseList(sysMenus);
    }
}
